/**
 * 弹窗插件
 * @author 凌晨@AmsWait
 * @description
 * MessageBox 的作用是替代系统自带的alert、confirm，仅适合展示较为简单的内容。如果需要弹出较为复杂的内容，请使用定制的弹窗。
 * @example
 * 请查看demo文件
 * @date 更新时间 2024年11月7日18点47分
 * @todo 连续调用时背景每次都会调用显示隐藏的动画，导致视觉上会有闪烁。
 */

(function (global, factory) {
    global = global || self;
    var obj = factory();
    for (var key in obj) {
        global[key] = obj[key];
    }
}(this, function () {
    'use strict';

    // 优先使用shadowDom
    function createDomRoot() {
        if (typeof window.ShadowRoot === 'function') {
            var host = document.createElement('div');
            document.body.appendChild(host);
            var shadowRoot = host.attachShadow({ mode: 'open' });
            return shadowRoot;
        } else {
            return document.body
        }

    }
    var DomRoot = createDomRoot();

    //#region  ie11兼容相关代码
    // Object.assign兼容处理
    if (typeof Object.assign != 'function') {
        Object.defineProperty(Object, "assign", {
            value: function assign(target, varArgs) {
                if (target == null) {
                    throw new TypeError('Cannot convert undefined or null to object');
                }

                var to = Object(target);

                for (var index = 1; index < arguments.length; index++) {
                    var nextSource = arguments[index];
                    if (nextSource != null) {
                        for (var nextKey in nextSource) {
                            if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                                to[nextKey] = nextSource[nextKey];
                            }
                        }
                    }
                }
                return to;
            },
            writable: true,
            configurable: true
        });
    }
    /**
     * classList.add|remove兼容处理
     * ie11下 classList.add|remove方法不支持一次传入多个参数比如classList.add('a', 'b', 'c');
     * @param {Element} element 操作的元素
     * @param {String} action  操作类型 add|remove
     * @param {String} classNames 待操作的className
     */
    function modifyClass(element, action, classNames) {
        classNames.split(' ').forEach(function (className) {
            element.classList[action](className);
        });
    }
    //#endregion

    var core = {
        box: null,
        mask: null,
        wrapper: null,
        title: null,
        msg: null,
        confirmBtn: null,
        cancelBtn: null,
        callback: null,
        defaultOptions: {
            title: '提示',
            options: {
                confirmButtonText: '确认',
                cancelButtonText: '取消',
                callback: null
            }
        },
        init: function () {
            this.box = DomRoot.querySelector('#_messageBox');
            this.mask = DomRoot.querySelector('#_messageBox_mask');
            this.wrapper = DomRoot.querySelector('._messageBox_wrapper');
            this.title = DomRoot.querySelector('#_messageBoxTitle');
            this.msg = DomRoot.querySelector('#_messageBoxMsg');
            this.confirmBtn = DomRoot.querySelector('#_messageBoxConfirm');
            this.cancelBtn = DomRoot.querySelector('#_messageBoxCancel');

            var _this = this;
            var clickHandler = {
                '_messageBoxClose': function () {
                    core.close();
                    _this.callback && _this.callback(false, 'close');
                },
                '_messageBoxCancel': function () {
                    core.close();
                    _this.callback && _this.callback(false, 'cancel');
                },
                '_messageBoxConfirm': function () {
                    core.close();
                    _this.callback && _this.callback(true, 'confirm');
                }
            };

            this.box.addEventListener('click', function (e) {
                clickHandler.hasOwnProperty(e.target.id) && clickHandler[e.target.id]()
            })

            // 动画结束后移除动画类名
            this.wrapper.addEventListener('animationend', function (e) {
                if (e.animationName === '__messageBox__fadeOutUp') _this.box.classList.add('hide');
                if (e.animationName === '__messageBox__fadeInDown') _this.box.classList.remove('hide');
                modifyClass(this, 'remove', '__messageBox__animate__animated __messageBox__animate__fadeOutUp __messageBox__animate__fadeInDown')
            }, false);

            this.mask.addEventListener('animationend', function (e) {
                modifyClass(this, 'remove', '__messageBox__animate__animated __messageBox__animate__fadeOut __messageBox__animate__fadeIn')
            }, false);
        },
        close: function () {
            modifyClass(this.wrapper, 'add', '__messageBox__animate__animated __messageBox__animate__fadeOutUp')
            modifyClass(this.mask, 'add', '__messageBox__animate__animated __messageBox__animate__fadeOut')
        },
        open: function (msg, title, options) {
            function validateType(value, types, paramName) {
                types = types || ['string', 'number'];
                if (!types.some(function (type) { return typeof value === type })) {
                    console.error('message-box： 请检查配置，' + paramName + '只能是' + types.join('或') + '类型');
                    return false;
                }
                return true;
            }

            if (!msg) return console.error('message-box： msg是必填项');
            if (!validateType(msg, null, 'msg')) return;
            if (title && !validateType(title, null, 'title')) return;
            if (options && options.confirmButtonText && !validateType(options.confirmButtonText, null, 'options.confirmButtonText')) return;
            if (options && options.cancelButtonText && !validateType(options.cancelButtonText, null, 'options.cancelButtonText')) return;
            if (options && options.callback && !validateType(options.callback, ['function'], 'options.callback')) return;

            // 填充默认文案
            this.title.innerHTML = title || this.defaultOptions.title;
            options = Object.assign({}, this.defaultOptions.options, options || {})

            this.msg.innerHTML = msg;

            options && options.confirmButtonText && (this.confirmBtn.innerHTML = options.confirmButtonText);
            options && options.cancelButtonText && (this.cancelBtn.innerHTML = options.cancelButtonText);

            this.callback = options && options.callback || null;

            this.box.classList.remove('hide');

            modifyClass(this.mask, 'add', '__messageBox__animate__animated __messageBox__animate__fadeIn')
            modifyClass(this.wrapper, 'add', '__messageBox__animate__animated __messageBox__animate__fadeInDown')
        }
    }

    /**
     * 插入css
     * @param {*} cb
     */
    var insertCss = function (cb) {
        var path;
        // 获取放置插件的路径
        if (document.currentScript) {
            path = document.currentScript.src;
        } else {
            // document.currentScript 不存在时，使用 document.scripts
            // 这里假设当前执行的脚本就是最后一个被添加的脚本
            for (var i = 0; i < document.scripts.length; i++) {
                var e = document.scripts[i];
                if (e.src.indexOf('/message-box.') !== -1) {
                    path = e.src
                }
            }
        }

        var link = document.createElement('link');
        link.rel = 'stylesheet';
        link.type = 'text/css';
        link.href = path.replace('js', 'css');

        DomRoot.appendChild(link);

        link.addEventListener('load', function () {
            cb && cb()
            core.box.style = '';
        })

        // 检测CSS文件加载过程中是否有错误
        link.addEventListener('error', function () {
            // 检查下是不是显式引用了css文件并且生效了
            var checkAgain;
            for (var i = 0; i < document.styleSheets.length; i++) {
                var styleSheet = document.styleSheets[i];
                if (styleSheet.href && styleSheet.href.indexOf('/message-box.') !== -1) {
                    // 对于跨域的样式表无法读取其cssRules规则，所以只检测下是否引用了
                    checkAgain = true;
                }
            }

            !checkAgain && console.error('message-box: CSS文件加载失败，请检查CSS文件是否放在JS同级目录下，或使用link标签显式引用CSS文件。');
        });
    }

    /**
     * 插入dom结构
     * @param {*} cb
     */
    var createMessageDom = function (cb) {
        var dialogsBox = document.createElement("div");
        dialogsBox.id = "_messageBox";
        dialogsBox.className = 'hide';
        dialogsBox.style.display = 'none';

        dialogsBox.innerHTML =
            '<div id="_messageBox_mask"></div>' +
            '<div class="_messageBox_wrapper">' +
            '<div class="_messageBox_header">' +
            '<div id="_messageBoxTitle">提示</div>' +
            '<svg id="_messageBoxClose" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">' +
            '<path d="M512 432.46249999L865.92499999 78.49999973a56.13749998 56.13749998 0 0 1 79.53750003-5e-8 56.17500029 56.17500029 0 0 1 0 79.53750002L591.49999971 512l353.925 353.92499999c21.97500029 21.97500029 22.12499972 57.4125003 0 79.53750003a56.17500029 56.17500029 0 0 1-79.53750004 0L512 591.49999971 158.07500001 945.50000027a56.13749998 56.13749998 0 0 1-79.53750003 5e-8 56.17500029 56.17500029 0 0 1 0-79.53750002L432.50000029 512 78.49999973 158.07500001a56.13749998 56.13749998 0 0 1-5e-8-79.53750003 56.17500029 56.17500029 0 0 1 79.53750002 0L512 432.50000029z"></path>' +
            '</svg>' +
            '</div>' +
            '<div id="_messageBoxMsg"></div>' +
            '<div class="_messageBox_btns">' +
            '<span id="_messageBoxCancel">取消</span>' +
            '<span id="_messageBoxConfirm">确定</span>' +
            '</div>' +
            '</div>';
        DomRoot.appendChild(dialogsBox);

        core.init()
    };


    insertCss();
    createMessageDom();

    return {
        messageBox: core,
        $close: core.close,
        /**
         * alert 默认不显示取消按钮
         * @param {String} msg 提示信息
         * @param {String} title 标题
         * @param {Object} options 配置项
         * @param {String} options.confirmButtonText 确认按钮文本
         * @param {String} options.callback 回调函数
         */
        $alert: function (msg, title, options) {
            var that = core;

            that.cancelBtn.classList.add('hide');

            that.open(msg, title, options)
        },
        /**
         * confirm
         * @param {String} msg 提示信息
         * @param {String} title 标题
         * @param {Object} options 配置项
         * @param {String} options.confirmButtonText 确认按钮文本
         * @param {String} options.cancelButtonText 取消按钮文本
         * @param {Function} options.callback 回调函数
         */
        $confirm: function (msg, title, options) {
            var that = core;

            that.cancelBtn.classList.remove('hide');

            that.open(msg, title, options);
        }
    };
}))
